home *** CD-ROM | disk | FTP | other *** search
- /*
- * This sample program awaits an ATP connection from a client. When it gets one
- * it spins off a new thread to handle the conversation with the client, which
- * can include an arbitrary number of queries regarding certain server information.
- * The server can handle up to 128 simultaneous clients.
- *
- * This code is based on the ATPSERV code provided in the Novell NLM SDK
- *
- * Written by: Jamie Osborne 9/94
- *
- * You may copy and modify this code at will.
- *
- * Please note that this NLM is intended at a demonstration program. At the time
- * of its creation, NetWare for PowerPC was in alpha. As such, this program
- * may not compile and run without changes in the final product.
- *
- * Apple Computer, Inc. makes no warranties about the the fitness of this code
- * for any purpose and will not be held liable for any damages you may suffer
- * from using this code. Furthermore, Apple Computer, Inc. will not be held
- * liable for the death of Elvis or any other space alien.
- *
- * Your mileage may vary.
- */
-
- // Some standard includes
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <string.h>
- #include <nwenvrn.h>
- #include <signal.h>
-
- /* You can compile this file with the Watcom C32 compiler to build
- it for NetWare running on an Intel-type machine. However,
- at the time this program was written, the NetWare interfaces
- for Intel had not been updated to 4.1.
- */
-
- #ifdef OLD_INTERFACES
- #include <process.h>
- #include <conio.h>
- #else
- #include <nwthread.h>
- #include <nwerrno.h>
- #endif
-
- // These includes are all necessary for using Apple Transaction Protocol
- #include <appletlk.h>
- #include <atp.h>
- #include <nbp.h>
- #include <ddp.h>
- #include <zip.h>
-
- /* Globals */
- ATEntity_t thisEntity; // The server's NBP entity
- int threadCount = 0; // A count of the number of spawned threads
- int exitingNLM = FALSE; // Flag set when the user unloads the NLM
- int *my_fd; // The main fd used for listening for new connections
-
- // We just prototype a few functions here instead of making a .h file
- extern int advertise(int fd,char *nbp_name); // Advertises our server with NBP
- void CleanUpFunc( int ); // Called on a Signal
- void HandleClientSession(int *client_fd); // Handles query/response with a client
- int SpinNewSession(ATAtpPass_t *req_pass); // Creates a new thread for a client connection
- int HandleRequest(char *input, char *output, int reqNum); // Interprets client requests
-
- #define kServerType ":ATDEMO_Server.NATIVE@"
- enum {STREAM_ERROR = -1};
- enum {kRequestedName = 2, kRequestedConnectionsInUse, kRequestedPeakConnections, kRequestedQuit};
-
-
- void main()
- {
- ATSocket sock = 0; // The server's AppleTalk socket.
- int err = 0;
- int flags = 0;
- char myType[52] = kServerType; // NBP type
- char myServer[128]; // Server name.
- int completionCode;
- ATAtpPass_t rcv_pass; // Used for incoming requests
- ATInet_t info; // The server's AppleTalk internet address.
- ATNvestr_t myZone; // The server's zone.
- ATInet_t router_info; // The router's AppleTalk internet address (if there if a router).
-
- // printf will print to the screen automatically created for the NLM when it is loaded
- printf( "AppleTalk Demo Server\n");
-
- // Register handlers for these various signals
- signal (SIGINT,CleanUpFunc);
- signal (SIGABRT,CleanUpFunc);
- signal (SIGTERM,CleanUpFunc);
-
- // Allocate the file descriptor used for incoming connections
- my_fd = (int *)malloc(sizeof(int));
-
- // Open a socket dynamically
- if( err = ATAtpOpen(my_fd,&sock) )
- {
- printf("ATAtpOpen failed: err = %x\n",err);
- exit(__LINE__); // Abort
- }
-
-
- // Get our address from the router
- if(err = ATDdpNetinfo(*my_fd,&info,&router_info,&flags) )
- {
- printf("ATDdpNetinfo failed: err = %x\n", err);
- ATAtpClose(*my_fd);
- free(my_fd);
- exit(__LINE__);
- }
-
- // Add "*" as the zone name.
- strncat(myType,"*",1);
-
-
- // Register the server's name and advertise it on the internet.
- GetFileServerName(0, myServer);
- strcat( myServer, myType ); // Concatenate our NBP type onto the name
-
- if (advertise( *my_fd, myServer ) == STREAM_ERROR)
- {
- printf("Advertise function returned Stream Error");
- ATAtpClose(*my_fd);
- free(my_fd);
- exit(__LINE__);
- }
-
- // Print our AppleTalk address to the screen
- printf("%s's AppleTalk address is: (%d.%d.%d)\n", myServer,info.net,info.node,info.socket);
-
- // Get the AppleTalk zone the server lives in
- if(err = ATZipGetMyZone(&myZone) )
- {
- printf("ATZipGetMyZone failed: err = %x\n",err);
- ATAtpClose(*my_fd);
- free(my_fd);
- exit(__LINE__);
- }
-
-
- myZone.str[myZone.len] = 0; // NULL terminate the string. (AppleTalk silliness)
-
- printf("The server is registered in the zone name: %s\n",myZone.str);
- printf("Waiting for request...\n\n");
-
- // Allcate memory for the receive buffer.
- // If this is not done, the server will crash when you receive a connection
- rcv_pass.data = (void *)malloc(ATP_DATA_SIZE);
- rcv_pass.data_len = ATP_DATA_SIZE;
-
- // Begin our loop waiting for initial connections
- do{
-
- /* This is the main point where our program will spend most of its time.
- The main thread for this NLM will block here until it receives an
- ATP request directed at our server
- */
- if(err = ATAtpGet(*my_fd,&rcv_pass) )
- {
- printf("ATAtpGet failed: err = %x\n",err);
- exitingNLM = TRUE;
- break;
- }
-
- // Check the return error from the packet. This is a protocol error
- if(rcv_pass.ret)
- printf("rcv_pass.ret returned error (%x)\n",rcv_pass.ret);
-
- // Print the AppleTalk address of the sender
- printf("Received message from a client at %u.%u.%u.\n",
- rcv_pass.at_addr.net,
- rcv_pass.at_addr.node,
- rcv_pass.at_addr.socket);
-
- // Print the message from the client
- printf("Received:%s\n",rcv_pass.data);
-
- // Spin off a new thread to handle this session
- err = SpinNewSession(&rcv_pass);
- if (err == EFAILURE)
- {
- printf("SpinNewSession failed: err = %x\n",errno);
- exitingNLM = TRUE;
- }
-
- }while (!exitingNLM);
-
- ATAtpClose(*my_fd);
- free(my_fd);
- free(rcv_pass.data);
- }
-
-
- /* SpinNewSession is called by main(). It opens a new socket,
- creates a new thread, and tells the client where (to which socket)
- it should send all of its requests. The new thread then
- handles the requests, allowing the main loop to await new ones.
- */
- int SpinNewSession(ATAtpPass_t *rcv_pass)
- {
- ATSocket sock = 0; // The new socket
- int err;
- int *new_fd; // The new file descriptor for the socket
- int threadID; // Thread ID of the new thread
- ATAtpPass_t resp_pass; // For sending our first response
- char respBuffer[256];
- int completionCode;
-
- printf("In SpinNewSession...\n");
-
- // Allocate room for a new file descriptor
- new_fd = (int *)malloc(sizeof(int));
-
- // Open a new socket for this client only
- if( err = ATAtpOpen(new_fd,&sock) )
- {
- printf("ATAtpOpen failed: err = %x\n",err);
- free(new_fd);
- return EFAILURE;
- }
- printf("Opened socket: %d, fd :%d\n", sock, *new_fd);
-
- // Send back a response, including which socket the client
- // should send further requests to
- threadID = GetThreadID();
- sprintf(respBuffer, "You are being handled by socket:%d", sock);
-
- resp_pass.at_addr = rcv_pass->at_addr; // The destination is the rcv source
-
- // Just to keep the console informed
- printf("Sending response to client at %u.%u.%u.\n",
- resp_pass.at_addr.net,
- resp_pass.at_addr.node,
- resp_pass.at_addr.socket);
-
-
- resp_pass.bitmap = 1; // We're sending 1 packet
- resp_pass.packetize = 0; // No need to packetize
- resp_pass.retry.interval = 2000; // 2000 ticks to retry
- resp_pass.retry.retries = 10;
- resp_pass.retry.backoff = 2;
- resp_pass.ret =0;
- resp_pass.data = respBuffer; // Our string
- resp_pass.data_len = strlen(respBuffer)+1; // The string's length plus on for the NULL
- resp_pass.userdata[0] = sock; // *** Store the new socket in the user data. A handy place for ints
- resp_pass.xo = 1; // We want this sent exactly once
- resp_pass.xo_relt = ATP_XO_DEF_REL_TIME;
- resp_pass.TransID = rcv_pass->TransID; // Match the TID from the client
-
- // Send back our first response on the old file descriptor
- printf("Sending first thread response on old fd %d.\n", *my_fd);
- if( err = ATAtpSendRsp(*my_fd,&resp_pass))
- {
- printf("ATAtpRsp failed: err = %x\n",err);
- free(new_fd);
- return EFAILURE;
- }
-
- // Begin a new thread, allowing it to create its own stack.
- completionCode = BeginThread(HandleClientSession, NULL, 0, new_fd);
- if (completionCode != ENOMEM && completionCode != EINVAL)
- threadCount++; // Increment our global thread counter
-
- return completionCode;
- }
-
-
- /* HandleClientSession is called by the thread that is created by
- SpinNewSession. It is similar to our main loop, but it handles
- interaction with a single client. When the client tells us its
- finished, or the some abnormal condition happens, we terminate
- the thread. Note that if the function returns, the thread is
- terminated by the system
- */
- void HandleClientSession(int *client_fd)
- {
- ATAtpPass_t rcv_pass, resp_pass; // For receiving and responding
- int err, quitRequest; // quitRequest is used to determine if the client is done
- char respBuffer[128]; // Buffer for our response strings
-
- // Allocate room for our receive buffer
- rcv_pass.data = (void *)malloc(ATP_DATA_SIZE);
- rcv_pass.data_len = ATP_DATA_SIZE;
-
- do
- {
- // This Get will block until our client sends a request for information.
- // If we never get a request, this call never returns
- printf("In client handling thread: Calling ATAtpGet fd:%d.\n", *client_fd);
- if(err = ATAtpGet(*client_fd,&rcv_pass) )
- {
-
- printf("ATAtpGet failed: err = %x\n",err);
- ATAtpClose(*client_fd); // We own the fd when we're called
- free(rcv_pass.data);
- free(client_fd);
- threadCount--;
- return; // This will kill the thread
- }
-
- // The get was successful, but there was a protocol error.
- if(rcv_pass.ret)
- printf("rcv_pass.ret returned error (%x)\n",rcv_pass.ret);
-
- // Show who the message was from
- printf("Received message from a client at %d.%d.%d.\n",
- rcv_pass.at_addr.net,
- rcv_pass.at_addr.node,
- rcv_pass.at_addr.socket);
-
- // Print what we got
- printf("Received:%s\n",rcv_pass.data);
- printf("Userdata: %d\n", rcv_pass.userdata[0]);
-
- // Interpret the request and get the appropriate response in the response buffer
- // Requests are sent as integers in the userdata field
- quitRequest = HandleRequest((char *)rcv_pass.data, &respBuffer, rcv_pass.userdata[0]);
-
- // Send back a response
- resp_pass.at_addr = rcv_pass.at_addr;
- resp_pass.bitmap = 1;/* Sending 1 packet. */
- resp_pass.packetize = 0;
- resp_pass.retry.interval = 2000;
- resp_pass.retry.retries = 2;
- resp_pass.retry.backoff = 2;
- resp_pass.ret =0;
- resp_pass.data = respBuffer;
- resp_pass.data_len = strlen(respBuffer)+1;
- resp_pass.userdata[0] = 0;
- resp_pass.xo = 1;
- resp_pass.xo_relt = ATP_XO_DEF_REL_TIME;
- resp_pass.TransID = rcv_pass.TransID;
-
-
- printf("Sending thread response on fd %d.\n", *client_fd);
- if( err = ATAtpSendRsp(*client_fd,&resp_pass))
- {
- printf("ATAtpRsp failed: err = %x\n",err);
- ATAtpClose(*client_fd);
- free(rcv_pass.data);
- free(client_fd);
- threadCount--;
- return;
- }
-
- if (quitRequest)
- {
- printf("Client requested quit\n");
- ATAtpClose(*client_fd);
- free(rcv_pass.data);
- free(client_fd);
- threadCount--;
- return;
- }
- }while ( !exitingNLM );
-
-
- // We will only reach this point if exitingNLM is true. That means someone unloaded us
- // at the console
-
- // Close the ATP socket gracefully.
- if(err = ATAtpClose(*client_fd))
- {
- printf("ATAtpClose failed: err = %x\n", err);
- }
-
- free(rcv_pass.data);
- free(client_fd);
- threadCount--;
- }
-
- /* HandleRequest interprets the the request number supplied and gets the
- appropriate answer. If the client requests to quit, the function returns 1,
- otherwise it returns 0.
- */
- int HandleRequest(char *input, char *output, int reqNum)
- {
- int ccode, structsize = 128; // For calls to get server info
- FILE_SERV_INFO sbuf; // Server info data structure
- int requestedQuit = 0;
-
- // Get some generic information about the current server status.
- ccode = GetServerInformation(structsize, &sbuf);
- if (ccode != 0)
- {
- printf("GetServerInformation returned an error! %d.\n", ccode);
- return 1; // this is OK even if ccode is 1. We just want to kill this thread
- }
-
- // Print the request number
- printf("ReqNum: %d\n", reqNum);
-
- // Interpret it
- switch(reqNum)
- {
- case kRequestedName:
- strcpy(output, sbuf.serverName);
- break;
- case kRequestedConnectionsInUse:
- sprintf(output,"%d", sbuf.connectionsInUse);
- break;
- case kRequestedPeakConnections:
- sprintf(output,"%d", sbuf.peakConnectionsUsed);
- break;
- case kRequestedQuit:
- strcpy(output, "Bye bye client.");
- requestedQuit = 1;
- break;
- default:
- strcpy(output, "Unknown request.");
- break;
- }
-
- printf("In HandleRequest: input: %s output: %s\n", input, output);
-
- return requestedQuit;
- }
-
-
-
-
- /* The advertise() function advertises the server's name on the network. The
- function parses the name string ServerName:ATP_Server.NATIVE@zonename
- into an nbp name with a type, object, and zone. Then, it
- registers the name with the internet. */
- int advertise( int fd, char * nbp_name)
- {
- int err;
- ATRetry_t retry;
-
- /*
- * Set up the structure that holds the timeout and retries.
- */
-
- retry.interval = 1000; /* Timeout Value. */
- retry.retries = 8; /* Number of retries.*/
- retry.backoff = 2; /* Gradually increases in time between retries. */
-
- if (err = ATNbpParseEntity(&thisEntity, nbp_name))
- {
- printf("ATNbpParseEntity failed: err = %x\n",err);
- return STREAM_ERROR; /* Clean up and exit is done in main. */
- }
- if (err = ATNbpRegister( &thisEntity, fd, (ATRetry_t *)&retry))
- {
- printf("ATNbpRegister failed: err = %x\n",err);
- return STREAM_ERROR; /* Clean up and exit is done in main. */
- }
-
- return 0;
- }
-
- /*
- CleanUpFunc is registered at the beginning of our program. If
- any of these signal conditions happen, the appropriate code will
- be executed. If the NLM is unloaded at the console, SIGTERM is
- raised.
- */
- void CleanUpFunc(int signalCondition)
- {
- int err;
- int i = 0;
-
- switch(signalCondition)
- {
- case (SIGTERM) :
- ConsolePrintf("Unload Detected in CleanUp Function!\n\r") ;
- exitingNLM = TRUE;
-
- ConsolePrintf("Calling ATNbpRemove...\n");
- if(err = ATNbpRemove(&thisEntity,*my_fd))
- {
- printf("ATNbpRemove failed: err = %x\n",err);
- exit(__LINE__);
- }
-
-
- // Close the ATP socket gracefully.
- ConsolePrintf("Calling ATAtpClose on %d\n", *my_fd);
- if(err = ATAtpClose(*my_fd))
- {
- printf("ATAtpClose failed: err = %x\n", err);
- exit(__LINE__);
- }
-
- // Give the various threads a chance to clean up
- // If it hasn't happened in 300 thread switches, give up.
- while(threadCount > 0 & i++ < 300)
- {
- ConsolePrintf("ThreadCount: %d\n", threadCount);
- ThreadSwitchWithDelay() ;
- }
-
- // Gime main() one last chance to clean up
- ThreadSwitchWithDelay() ;
- break ;
- case (SIGABRT) :
- case (SIGINT) :
- exit(__LINE__);
- break ;
- default :
- ConsolePrintf("Unknown Signal Condition\n\r") ;
- }
- }
-